/*
 * Copyright (c) sl All rights reserved.
 * 本软件源代码版权归sl所有,未经许可不得任意复制与传播.
 */
package com.sl.crawler.web;

import com.sl.crawler.common.utils.DateUtils;
import com.sl.crawler.common.enums.EmYn;
import com.sl.crawler.domain.common.Page;
import com.sl.crawler.service.base.BaseService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Date;

public abstract class BaseController<T, KEY extends Serializable> {
    private static final Logger LOG = LoggerFactory.getLogger(BaseController.class);

    /**
     * 获取Service操作类
     */
    public abstract BaseService<T, KEY> getService();

    /**
     * 获取视图基本路径 t
     */
    public abstract String getModelName();

    /**
     * 获取视图路径 t/t
     */
    public String getViewPath() {
        return getModelName() + "/" + getModelName();
    }

    /**
     * 获取导出csv的表头
     *
     * @return
     */
    public abstract String getExportCsvTitle();

    /**
     * 获取导出csv的数据明细
     *
     * @param t
     * @return
     */
    public abstract String getExportCsvRow(T t);

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(true));
    }

    /**
     * 列表展示
     *
     * @param t    实体对象
     * @param page 分页对象
     * @return
     */
    public String baseList(T t, Page<T> page, Model view) throws Exception {
        try {
            Class<?> clz = t.getClass();
            Object obj = clz.getMethod("getOrderField").invoke(t);
            if (obj == null || StringUtils.isBlank(String.valueOf(obj))) {
                clz.getMethod("setOrderField", String.class).invoke(t, "id");
                clz.getMethod("setOrderFieldType", String.class).invoke(t, "desc");
            }
            view.addAttribute(getModelName(), t);
            view.addAttribute("page", getService().selectPage(t, page));
        } catch (Exception e) {
            LOG.error("失败:" + e.getMessage(), e);
            if (e.getMessage().indexOf("Duplicate ") >= 0) {
                throw new Exception("唯一字段数据保存重复!");
            } else {
                throw e;
            }
        } finally {
        }
        return getViewPath() + "List";
    }

    /**
     * 响应新增(修改)页面
     *
     * @param key 对象编号
     * @return
     */
    public String baseEdit(KEY key, Model view) throws Exception {
        try {
            if (key != null && (Long) key != 0) {
                T t = getService().selectEntry(key);
                if (t == null) {
//					return toJSON(Message.failure("您要修改的数据不存在或者已被删除!"));
                    return null;
                }
                view.addAttribute(getModelName(), t);
            }
        } catch (Exception e) {
            LOG.error("失败:" + e.getMessage(), e);
            throw e;
        } finally {
        }

        return getViewPath() + "Edit";
    }

    /**
     * 通过编号删除对象
     *
     * @param key 对象编号
     * @return
     */
    public String baseDelete(KEY key, Model view) throws Exception {
        T t = getService().selectEntry(key);
        try {
            Class<?> clz = t.getClass();
            clz.getMethod("setYn", Integer.class).invoke(t, EmYn.NO.value());
        } catch (Exception e) {
            throw new Exception("设置删除参数yn失败", e);
        }
        int res = getService().saveOrUpdate(t);
        return "redirect:/" + getModelName();
    }

    /**
     * 通过编号查看对象
     *
     * @param key 对象编号
     * @return
     */
    public String baseView(KEY key, Model view) throws Exception {
        try {
            T t = getService().selectEntry(key);
            if (t == null) {
                return null;
            }
            view.addAttribute(getModelName(), t);
        } catch (Exception e) {
            LOG.error("失败:" + e.getMessage(), e);
            throw e;
        } finally {
        }

        return getViewPath() + "View";
    }

    /**
     * 保存方法
     *
     * @param t 实体对象
     * @return
     */
    public String baseSave(T t) throws Exception {
        try {
            int res = getService().saveOrUpdate(t);
        } catch (Exception e) {
            LOG.error("失败:" + e.getMessage(), e);
        } finally {
        }
        return "redirect:/" + getModelName();
    }


    /**
     * 导出CSV
     *
     * @param t
     * @return
     * @throws Exception
     */
    public void baseExportCsv(T t, Page<T> page, HttpServletResponse response) throws Exception {
        String csvFileName = getModelName() + "_" + DateUtils.format(new Date(), "yyyyMMddHHmmss") + ".csv";
        response.setHeader("Content-type", "text/csv;charset=gbk");
        response.setCharacterEncoding("gbk");

        String headerKey = "Content-Disposition";
        String headerValue = String.format("attachment; filename=\"%s\"", csvFileName);
        response.setHeader(headerKey, headerValue);
        getService().selectPage(t, page);
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.write(getExportCsvTitle());
            writer.write("\r\n");
            while (page.getCurrentPage() <= page.getPageCount()) {
                long t3 = System.currentTimeMillis();
                for (T result : page.getResult()) {
                    writer.write(getExportCsvRow(result));
                }
                writer.flush();
                response.getWriter().flush();
                page.setCurrentPage(page.getCurrentPage() + 1);
                getService().selectPage(t, page);
                long t4 = System.currentTimeMillis();
                LOG.info(String.format("%s 导出csv中, pageNo=%s, IO writer flush耗时毫秒：%s", getModelName(), page.getCurrentPage(), t4 - t3));
            }
            writer.flush();
            writer.close();
        } catch (Exception e) {
            LOG.error("导出CSV列表失败:", e);
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}

